home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Palettes / MiscSoundPalette / MiscSoundUtil.subproj / MiscVolumeMeter.m < prev    next >
Encoding:
Text File  |  1995-03-25  |  15.1 KB  |  715 lines

  1. /*
  2.  
  3. MiscVolumeMeter
  4. Version 1.2
  5. Copyright (c) 1995 by Sean Luke
  6. Donated to the MiscKit
  7.  
  8. Permission to use, copy, modify, and distribute this material 
  9. for any purpose and without fee, under the restrictions as noted 
  10. in the MiscKit copyright notice, is hereby granted, provided that
  11. the MiscKit copyright notice and this permission notice 
  12. appear in all source copies, and that the author's name shall not
  13. be used in advertising or publicity pertaining to this 
  14. material without the specific, prior written permission 
  15. of the author.  SEAN O. LUKE  MAKES NO REPRESENTATIONS ABOUT THE
  16. ACCURACY OR SUITABILITY OF THIS MATERIAL FOR ANY PURPOSE.  
  17. IT IS PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
  18.  
  19. */
  20.  
  21. #import "MiscVolumeMeter.h"
  22. #import <stdio.h>
  23.  
  24. @implementation MiscVolumeMeter
  25.  
  26. // slave method to the below timed entry function
  27.  
  28. - _update
  29.     {
  30.     if ([delegate respondsTo:@selector(meterWillUpdateOnOwn:)])
  31.         [delegate meterWillUpdateOnOwn:self];
  32.     return [self display];
  33.     }
  34.  
  35.  
  36. // timed entry procedure for periodically updating
  37.  
  38. DPSTimedEntryProc VOLUMEMETER_update_meter
  39.     (DPSTimedEntry teNum,double now, void* the_meter)
  40.     {
  41.     MiscVolumeMeter* temp_meter=(MiscVolumeMeter*) the_meter;
  42.     //printf ("Display!\n");
  43.     [temp_meter _update];
  44.     return (void*) NULL;
  45.     }
  46.  
  47.  
  48. // convenience functions
  49.  
  50. BOOL VOLUMEMETER_draw_wide(const NXRect* aRect)
  51.     {
  52.     return (BOOL) (aRect->size.width>=aRect->size.height);
  53.     }
  54.  
  55. BOOL VOLUMEMETER_can_draw(const NXRect* aRect)
  56.     {
  57.     return (BOOL) (aRect->size.width>VOLUMEMETER_VALUE_INSET*2
  58.         &&aRect->size.height>VOLUMEMETER_VALUE_INSET*2);
  59.     }
  60.  
  61.  
  62.  
  63. // methods
  64.  
  65. - initFrame:(const NXRect*) frameRect
  66.     {
  67.     int x;
  68.     
  69.     [super initFrame:frameRect];
  70.     
  71.     delegate=NULL;
  72.     input=NO;
  73.     running=NO;
  74.     bezeled=YES;
  75.     peak_bubble_displayed=YES;
  76.     stereo=YES;
  77.     background_gray=NX_DKGRAY;
  78.     value_gray=NX_LTGRAY;
  79.     bubble_gray=NX_WHITE;
  80.     refresh=VOLUMEMETER_TIMED_ENTRY_SPEED;
  81.     refreshes_per_new_peak_bubble=VOLUMEMETER_STD_REFRESHES;
  82.     refresh_tally=0;
  83.     for (x=0;x<VOLUMEMETER_MAX_REFRESHES;x++) 
  84.         {refreshes_left[x]=0.0;refreshes_right[x]=0.0;}
  85.     current_max_refresh_left=0;
  86.     current_max_refresh_right=0;
  87.     
  88.     if (input_device==NULL) input_device= [[NXSoundIn alloc] init];
  89.     if (output_device==NULL) output_device=[[NXSoundOut alloc] init];
  90.     [input_device setDetectPeaks:YES];
  91.     [output_device setDetectPeaks:YES];
  92.     teNum=0;
  93.     
  94.     return self;
  95.     }
  96.     
  97.     
  98.  
  99. - drawSelf:(const NXRect*) rects:(int) rectCount
  100.     {
  101.     NXRect drawRectLeft,drawRectRight;
  102.     NXRect backgroundRect=bounds;
  103.     NXRect valueRect=bounds;
  104.     float left,right;
  105.     int just_erase=0;
  106.     
  107.     //if (![window isVisible]) return NULL;        // no window to draw in.
  108.     // the above has been turned off because when loading Resound,
  109.     // the sound meter wouldn't display until a sound was being played
  110.     // or recorded!
  111.     
  112.     if ([delegate respondsTo:@selector(meterWillUpdate:)])
  113.         [delegate meterWillUpdate:self];
  114.     
  115.     // first to check to see if the sound lock is current
  116.     //printf ("Meter\n");
  117.     if (sound!=NULL)
  118.         {
  119.         int status=NX_SoundStopped;
  120.         id actual_sound=NULL;            // quiets compiler complaints
  121.         
  122.         if ([sound isKindOf:[Sound class]]) 
  123.             {actual_sound=sound;}
  124.         else if ([sound isKindOf:[SoundView class]])
  125.             {actual_sound=[sound soundBeingProcessed];}
  126.         status=[actual_sound status];
  127.         if (status==NX_SoundStopped||
  128.             status==NX_SoundInitialized||
  129.             status==NX_SoundFreed) 
  130.         {just_erase=1;}
  131.             
  132.         // Then modify the meter to match the sound    
  133.         
  134.         else if (status==NX_SoundRecordingPaused||
  135.                  status==NX_SoundRecording||
  136.                  status==NX_SoundRecordingPending)
  137.         {[self setToInput];}
  138.         
  139.         else if (status==NX_SoundPlayingPaused||
  140.                  status==NX_SoundPlaying||
  141.                  status==NX_SoundPlayingPending)
  142.         {[self setToOutput];}
  143.         
  144.         if ([actual_sound channelCount]>1)
  145.             {[self setStereo];}
  146.         else {[self setMono];}
  147.         }
  148.     
  149.     // then check for bezeled stuff
  150.     
  151.     
  152.     if (bezeled)
  153.         {
  154.         backgroundRect.origin.x        +=VOLUMEMETER_BACKGROUND_INSET; 
  155.         backgroundRect.size.width    -=VOLUMEMETER_BACKGROUND_INSET*2;
  156.         backgroundRect.origin.y        +=VOLUMEMETER_BACKGROUND_INSET; 
  157.         backgroundRect.size.height    -=VOLUMEMETER_BACKGROUND_INSET*2;
  158.         
  159.         valueRect.origin.x        +=VOLUMEMETER_VALUE_INSET;
  160.         valueRect.size.width     -=VOLUMEMETER_VALUE_INSET*2;
  161.         valueRect.origin.y        +=VOLUMEMETER_VALUE_INSET;
  162.         valueRect.size.height    -=VOLUMEMETER_VALUE_INSET*2;
  163.         }
  164.     else
  165.         {
  166.         valueRect.origin.x        +=
  167.                 VOLUMEMETER_VALUE_INSET-VOLUMEMETER_BACKGROUND_INSET;
  168.         valueRect.size.width     -=
  169.                 VOLUMEMETER_VALUE_INSET*2-VOLUMEMETER_BACKGROUND_INSET*2;
  170.         valueRect.origin.y        +=
  171.                 VOLUMEMETER_VALUE_INSET-VOLUMEMETER_BACKGROUND_INSET;
  172.         valueRect.size.height    -=
  173.                 VOLUMEMETER_VALUE_INSET*2-VOLUMEMETER_BACKGROUND_INSET*2;
  174.         }
  175.     
  176.     if (!VOLUMEMETER_can_draw(&bounds)) return self;    // can't draw
  177.     
  178.     if (bezeled) NXDrawGrayBezel(&bounds,NULL);
  179.     PSsetgray(background_gray);
  180.     NXRectFill(&backgroundRect);
  181.     
  182.     if (just_erase) return self;
  183.     
  184.     // compute for drawing
  185.     
  186.     if (running)
  187.         {
  188.         left=0;right=0;
  189.         
  190.         if (input&&input_device!=NULL) 
  191.             [input_device getPeakLeft:&left right:&right];
  192.         
  193.         if ((!input)&&output_device!=NULL)
  194.             [output_device getPeakLeft:&left right:&right];
  195.             
  196.         if (left>1) left=1; if (right>1) right=1;
  197.             // occasionally a NeXTSTEP bug returns values larger than 1!
  198.         
  199.         // perform refresh computations
  200.         
  201.         if (++refresh_tally>=refreshes_per_new_peak_bubble) refresh_tally=0;
  202.         refreshes_left[refresh_tally]=left;
  203.         refreshes_right[refresh_tally]=right;
  204.         if (left>=refreshes_left[current_max_refresh_left])
  205.     
  206.     // remember, this might simply be because left stepped on the old champion!
  207.     // ...search for new champion
  208.     
  209.             {
  210.             int y;
  211.             int maxpos=0;
  212.             for (y=0;y<refreshes_per_new_peak_bubble;y++)
  213.                 if (refreshes_left[y]>refreshes_left[maxpos]) maxpos=y;
  214.             current_max_refresh_left=maxpos;
  215.             }
  216.         if (right>=refreshes_right[current_max_refresh_right])
  217.             // same as above!
  218.             // ...search for new champion
  219.             {
  220.             int y;
  221.             int maxpos=0;
  222.             for (y=0;y<refreshes_per_new_peak_bubble;y++)
  223.                 if (refreshes_right[y]>refreshes_right[maxpos]) maxpos=y;
  224.             current_max_refresh_right=maxpos;
  225.             }
  226.         
  227.         // Draw away...
  228.             
  229.         if (VOLUMEMETER_draw_wide(&valueRect))        // draw wide
  230.             {
  231.                 
  232.             if (stereo)
  233.                 {
  234.                 
  235.                 // note that right and left are flipped,
  236.                 // so that when displaying wide, left is on the top.
  237.                 
  238.                 drawRectRight=valueRect;
  239.                 drawRectRight.size.height*=1-VOLUMEMETER_RIGHT_BEGIN;
  240.                 drawRectRight.origin.y+=valueRect.size.height*
  241.                         VOLUMEMETER_RIGHT_BEGIN;
  242.                 drawRectRight.size.width*=left;
  243.             
  244.                 drawRectLeft=valueRect;
  245.                 drawRectLeft.size.height*=VOLUMEMETER_LEFT_END;
  246.                 drawRectLeft.size.width*=right;
  247.                 }
  248.             else
  249.                 {
  250.                 drawRectRight=valueRect;
  251.                 drawRectRight.size.width*=(right+left)/2.0;
  252.                 }
  253.             }
  254.         else                                        // draw tall
  255.             {
  256.             
  257.             if (stereo)
  258.                 {
  259.                 drawRectRight=valueRect;
  260.                 drawRectRight.size.width*=1-VOLUMEMETER_RIGHT_BEGIN;
  261.                 drawRectRight.origin.x+=valueRect.size.width*
  262.                         VOLUMEMETER_RIGHT_BEGIN;
  263.                 drawRectRight.size.height*=right;
  264.             
  265.                 drawRectLeft=valueRect;
  266.                 drawRectLeft.size.width*=VOLUMEMETER_LEFT_END;
  267.                 drawRectLeft.size.height*=left;
  268.                 }
  269.             else
  270.                 {
  271.                 drawRectRight=valueRect;
  272.                 drawRectRight.size.height*=(right+left)/2.0;
  273.                 }
  274.             }
  275.         if (left+right>0.0)            
  276.         
  277.         // I go through the computation because peak bubbles need it
  278.         
  279.             {
  280.             PSsetgray(value_gray);
  281.             NXRectFill(&drawRectRight);
  282.             if (stereo) NXRectFill(&drawRectLeft);
  283.             }
  284.         // Draw Peak Bubbles            
  285.  
  286.         if (peak_bubble_displayed)
  287.             {
  288.             NXRect rightRect=drawRectRight;
  289.             NXRect leftRect=drawRectLeft;
  290.             float max_left=refreshes_left[current_max_refresh_left];
  291.             float max_right=refreshes_right[current_max_refresh_right];
  292.             
  293.             if (max_left+max_right>0.0)
  294.                 {
  295.             
  296.                 if (VOLUMEMETER_draw_wide(&valueRect))        // draw wide
  297.                     {
  298.                     rightRect.size.width=0.1;            // ...makes it a line
  299.                     leftRect.size.width=0.1;
  300.                     
  301.                     if (stereo)
  302.                         {
  303.                         rightRect.origin.x=floor(drawRectRight.origin.x+
  304.                             valueRect.size.width*max_left);
  305.                         leftRect.origin.x=floor(drawRectLeft.origin.x+
  306.                             valueRect.size.width*max_right);
  307.                         }
  308.                     else
  309.                         {
  310.                         rightRect.origin.x=floor(drawRectRight.origin.x+
  311.                             valueRect.size.width*
  312.                             (max_right+max_left)/2.0);
  313.                         }
  314.                     }    
  315.                 else                                        // draw tall
  316.                     {
  317.                     rightRect.size.height=0.1;                // makes it a line
  318.                     leftRect.size.height=0.1;
  319.                     
  320.                     if (stereo)
  321.                         {
  322.                         rightRect.origin.y=floor(drawRectRight.origin.y+
  323.                             valueRect.size.height*max_right);
  324.                         leftRect.origin.y=floor(drawRectLeft.origin.y+
  325.                             valueRect.size.height*max_left);
  326.                         }
  327.                     else
  328.                         {
  329.                         rightRect.origin.y=floor(drawRectRight.origin.y+
  330.                             valueRect.size.height*
  331.                             (max_right+max_left)/2.0);
  332.                         }
  333.                     }
  334.                 PSsetgray(bubble_gray);
  335.                 NXRectFill(&rightRect);
  336.                 if (stereo) NXRectFill(&leftRect);
  337.                 }
  338.             }
  339.         }
  340.     NXPing();
  341.     if ([delegate respondsTo:@selector(meterDidUpdate:)])
  342.         [delegate meterDidUpdate:self];
  343.     // else...
  344.     return self;
  345.     }
  346.  
  347.  
  348. - setMono
  349.     {
  350.     stereo=NO;
  351.     //[self display];
  352.     return self;
  353.     }
  354.     
  355. - setStereo
  356.     {
  357.     stereo=YES;
  358.     //[self display];
  359.     return self;
  360.     }
  361.  
  362. - setBackgroundGray:(float) this_value
  363.     {
  364.     if (this_value>=0&&this_value<=1) background_gray=this_value;
  365.     [self display];
  366.     return self;
  367.     }
  368.  
  369. - setValueGray:(float) this_value
  370.     {
  371.     if (this_value>=0&&this_value<=1) value_gray=this_value;
  372.     [self display];
  373.     return self;
  374.     }
  375.  
  376. - setBubbleGray: (float) this_value
  377.     {
  378.     if (this_value>=0&&this_value<=1) bubble_gray=this_value;
  379.     [self display];
  380.     return self;
  381.     }
  382.  
  383. - (float) backgroundGray
  384.     {
  385.     return background_gray;
  386.     }
  387.  
  388. - (float) valueGray
  389.     {
  390.     return value_gray;
  391.     }
  392.  
  393. - (float) bubbleGray
  394.     {
  395.     return bubble_gray;
  396.     }
  397.  
  398. - setBezeled:(BOOL) yes_or_no
  399.     {
  400.     bezeled=yes_or_no;
  401.     [self display];
  402.     return self;
  403.     }
  404.  
  405. - setPeakBubbleDisplayed:(BOOL) yes_or_no
  406.     {
  407.     peak_bubble_displayed=yes_or_no;
  408.     [self display];
  409.     return self;
  410.     }
  411.  
  412. - setToInput
  413.     {
  414.     input=YES;
  415.         // try to allocate once more...
  416.     [self reclaim];
  417.         // ...then test
  418.     if (input_device==NULL) return NULL;
  419.     return self;
  420.     }
  421.  
  422. - setToOutput
  423.     {
  424.     input=NO;
  425.         // try to allocate once more...
  426.     [self reclaim];
  427.         // ...then test
  428.     if (output_device==NULL) return NULL;
  429.     return self;
  430.     }
  431.  
  432. - setRefresh:(float) number_seconds
  433.     {
  434.     if (number_seconds>0)
  435.         {
  436.         refresh=number_seconds;
  437.         if (teNum) 
  438.             {
  439.             DPSRemoveTimedEntry(teNum);
  440.             teNum=DPSAddTimedEntry(refresh, 
  441.                 (DPSTimedEntryProc) VOLUMEMETER_update_meter,
  442.                 (void*) self, (int) NX_RUNMODALTHRESHOLD);
  443.             }
  444.         }
  445.     return self;
  446.     }
  447.     
  448. - setRefreshesPerNewPeakBubble:(int) number_refreshes
  449.     {
  450.     if (number_refreshes<=VOLUMEMETER_MAX_REFRESHES&&number_refreshes>0)
  451.         {
  452.         int x;
  453.         refreshes_per_new_peak_bubble=number_refreshes;
  454.         for (x=0;x<number_refreshes;x++) 
  455.             {refreshes_left[x]=0.0;refreshes_right[x]=0.0;}
  456.         current_max_refresh_left=0;
  457.         current_max_refresh_right=0;
  458.         refresh_tally=0;
  459.         }
  460.     return self;
  461.     }
  462.  
  463. - reclaim
  464.     {
  465.     // try to grab devices
  466.     
  467.     if (input_device==NULL) input_device=[[NXSoundIn alloc] init];
  468.     if (output_device==NULL) output_device=[[NXSoundOut alloc] init];
  469.     
  470.     // reset devices
  471.     
  472.     [input_device setDetectPeaks:YES];
  473.     [output_device setDetectPeaks:YES];
  474.     
  475.     // don't display here!  That would create a loop.
  476.     return self;
  477.     }
  478.  
  479. - run
  480.     {
  481.     running=YES;
  482.     //printf ("Run\n");
  483.     if (teNum) DPSRemoveTimedEntry(teNum);
  484.     teNum=DPSAddTimedEntry(refresh, 
  485.             (DPSTimedEntryProc) VOLUMEMETER_update_meter,
  486.             (void*) self, (int) NX_RUNMODALTHRESHOLD);
  487.     [self display];
  488.     return self;
  489.     }
  490.  
  491. - stop
  492.     {
  493.     running=NO;
  494.     //printf ("Stop\n");
  495.     if (teNum) DPSRemoveTimedEntry(teNum);
  496.     teNum=0;
  497.     [self display];
  498.     return self;
  499.     }
  500.     
  501. - read:(NXTypedStream*) stream
  502.     {
  503.     [super read:stream];
  504.     /*NXReadTypes(stream,"cccccfff",&input,&running,&bezeled,
  505.         &peak_bubble_displayed,&stereo,
  506.         &background_gray,&value_gray,&bubble_gray);*/
  507.     // Commented out to provide new read format:
  508.     NXReadTypes(stream,"ccccffffi",&input,&bezeled,
  509.         &peak_bubble_displayed,&stereo,
  510.         &background_gray,&value_gray,&bubble_gray,&refresh,
  511.         &refreshes_per_new_peak_bubble);
  512.     return self;
  513.     }
  514.  
  515. - write:(NXTypedStream*) stream
  516.     {
  517.     [super write:stream];
  518.     /*NXWriteTypes(stream,"cccccfff",&input,&running,&bezeled,
  519.         &peak_bubble_displayed,&stereo,
  520.         &background_gray,&value_gray,&bubble_gray);*/
  521.     // Commented out to provide new write format:
  522.     NXWriteTypes(stream,"ccccffffi",&input,&bezeled,
  523.         &peak_bubble_displayed,&stereo,
  524.         &background_gray,&value_gray,&bubble_gray,&refresh,
  525.         &refreshes_per_new_peak_bubble);
  526.     return self;
  527.     }
  528.  
  529. - free
  530.     {
  531.     if (teNum) DPSRemoveTimedEntry(teNum);
  532.     teNum=0;
  533.     if (input_device!=NULL) [input_device free];
  534.     if (output_device!=NULL) [output_device free];
  535.     return [super free];
  536.     }
  537.  
  538. - awake
  539.     {
  540.     int x;
  541.     
  542.     [super awake];
  543.     
  544.     refresh=VOLUMEMETER_TIMED_ENTRY_SPEED;
  545.     refreshes_per_new_peak_bubble=VOLUMEMETER_STD_REFRESHES;
  546.     refresh_tally=0;
  547.     for (x=0;x<VOLUMEMETER_MAX_REFRESHES;x++) 
  548.         {refreshes_left[x]=0.0;refreshes_right[x]=0.0;}
  549.     current_max_refresh_left=0;
  550.     current_max_refresh_right=0;
  551.     
  552.     if (input_device==NULL) input_device= [[NXSoundIn alloc] init];
  553.     if (output_device==NULL) output_device=[[NXSoundOut alloc] init];
  554.     [input_device setDetectPeaks:YES];
  555.     [output_device setDetectPeaks:YES];            // for symmetry;
  556.  
  557.     //if (running) [self run];
  558.     return self;
  559.     }
  560.  
  561. - setMono:sender
  562.     {
  563.     return [self setMono];
  564.     }
  565.  
  566. - setStereo:sender
  567.     {
  568.     return [self setStereo];
  569.     }
  570.  
  571. - setToInput:sender
  572.     {
  573.     return [self setToInput];
  574.     }
  575.  
  576. - setToOutput:sender
  577.     {
  578.     return [self setToOutput];
  579.     }
  580.  
  581. - run:sender
  582.     {
  583.     return [self run];
  584.     }
  585.  
  586. - stop:sender
  587.     {
  588.     return [self stop];
  589.     }
  590.  
  591. - windowDidBecomeKey:sender
  592.     {
  593.     id temp=self;
  594.     //if ([delegate respondsTo:@selector(windowDidBecomeKey:)])
  595.     //    temp=[delegate windowDidBecomeKey:sender];
  596.     if (temp!=NULL) [self reclaim];
  597.     if (temp!=NULL) [self run];
  598.     return self;
  599.     }
  600.     
  601.     
  602. - windowDidBecomeMain:sender
  603.     {
  604.     id temp=self;
  605.     //if ([delegate respondsTo:@selector(windowDidBecomeMain:)])
  606.     //    temp=[delegate windowDidBecomeMain:sender];
  607.     if (temp!=NULL) [self reclaim];
  608.     if (temp!=NULL) [self run];
  609.     return self;
  610.     }
  611.     
  612.     
  613.  
  614. - windowDidDeminiaturize:sender
  615.     {
  616.     id temp=self;
  617.     //if ([delegate respondsTo:@selector(windowDidDeminiaturize:)])
  618.     //    temp=[delegate windowDidDeminiaturize:sender];
  619.     if (temp!=NULL) [self reclaim];
  620.     if (temp!=NULL) [self run];
  621.     return self;
  622.     }
  623.     
  624.     
  625.  
  626. - windowDidMiniaturize:sender
  627.     {
  628.     id temp=self;
  629.     //if ([delegate respondsTo:@selector(windowDidMiniaturize:)])
  630.     //    temp=[delegate windowDidMiniaturize:sender];
  631.     if (temp!=NULL) [self stop];
  632.     return self;
  633.     }
  634.     
  635.     
  636. - windowWillClose:sender
  637.     {
  638.     id temp=self;
  639.     //if ([delegate respondsTo:@selector(windowWillClose:)])
  640.     //    temp=[delegate windowWillClose:sender];
  641.     if (temp!=NULL) [self stop];
  642.     return self;
  643.     }
  644.     
  645. - setSound:this_sound
  646.     {
  647.     sound=this_sound;
  648.     [self display];
  649.     return self;
  650.     }
  651.  
  652. - sound
  653.     {
  654.     return sound;
  655.     }
  656.     
  657. - setDelegate:this_delegate
  658.     {
  659.     delegate=this_delegate;
  660.     return self;
  661.     }
  662.     
  663. - delegate
  664.     {
  665.     return delegate;
  666.     }
  667.     
  668.  
  669. - (BOOL) isBezeled:sender
  670.     {
  671.     return bezeled;
  672.     }
  673.     
  674. - (BOOL) peakBubbleDisplayed:sender
  675.     {
  676.     return peak_bubble_displayed;
  677.     }
  678.     
  679.  
  680. - (BOOL) isInput:sender
  681.     {
  682.     return input;
  683.     }
  684.     
  685.  
  686. - (BOOL) isStereo:sender
  687.     {
  688.     return stereo;
  689.     }
  690.     
  691.  
  692. - (float) refresh:sender
  693.     {
  694.     return refresh;
  695.     }
  696.     
  697.  
  698. - (int) refreshesPerPeakBubble:sender
  699.     {
  700.     return refreshes_per_new_peak_bubble;
  701.     }
  702.     
  703.     
  704. // The following are delegate methods, just listed here to stop warnings
  705. - meterWillUpdateOnOwn:sender {return NULL;}
  706. - meterWillUpdate:sender {return NULL;}    
  707. - meterDidUpdate:sender    {return NULL;}    
  708.     
  709. - (const char*) getInspectorClassName
  710.     {
  711.     return "MiscVolumeMeterInspector";
  712.     }
  713.     
  714. @end
  715.